package pl.edu.agh.mguesser.game.stop;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import pl.edu.agh.mguesser.exception.MusicGuesserRuntimeException;
import pl.edu.agh.mguesser.game.listener.StopRequestListener;
import pl.edu.agh.mguesser.game.listener.TimeTickingListener;

/**
 * @author keiran
 */
public class StopRequestHandlerImpl implements StopRequestHandler {

    private int timeout = DEFAULT_TIMEOUT;
    
    private boolean disposed = false;
    private volatile boolean stopped = false;
    private volatile boolean timedOut = false;
    
    private volatile AtomicInteger seconds = new AtomicInteger(0);
    
    private ScheduledExecutorService executor 
            = Executors.newScheduledThreadPool(2, new ThreadFactoryBuilder().setDaemon(true).build());
    
    private Lock lock = new ReentrantLock();
    private Condition gotStopRequest = lock.newCondition();
    
    private StopRequestListener stopRequestListener;
    private TimeTickingListener timeTickingListener;

    public StopRequestHandlerImpl(StopRequestListener stopRequestListener,
                                  TimeTickingListener timeTickingListener) {
        this.stopRequestListener = stopRequestListener;
        this.timeTickingListener = timeTickingListener;
    }
    
    @Override
    public int waitForStop() {
        lock.lock();
        
        try {
            if(disposed) {
                throw new IllegalStateException();
            }
            
            timeTickingListener.tick(0);
            
            executor.schedule(new ListenerTask(), 0, TimeUnit.MILLISECONDS);
            executor.scheduleAtFixedRate(new TimeCountingTask(), 1000, 1000, TimeUnit.MILLISECONDS);
            
            while(!timedOut && !stopped) {
                timedOut = !(gotStopRequest.await(timeout, TimeUnit.MILLISECONDS));
            }
            
            return seconds.get();
            
        } catch(InterruptedException ex) {
            throw new MusicGuesserRuntimeException(ex);
            
        } finally {
            dispose();
            lock.unlock();
            
        }
        
    }

    @Override
    public void gameDone() {
        dispose();
    }

    private void dispose() {
        executor.shutdownNow();
        disposed = true;
    }

    public boolean isDisposed() {
        return disposed;
    }

    public StopRequestListener getStopRequestListener() {
        return stopRequestListener;
    }

    public TimeTickingListener getTimeTickingListener() {
        return timeTickingListener;
    }

    public boolean isStopped() {
        return stopped;
    }

    public boolean isTimedOut() {
        return timedOut;
    }

    ScheduledExecutorService getExecutor() {
        return executor;
    }

    void setTimeout(int timeout) {
        this.timeout = timeout;
    }
    
    private class ListenerTask implements Runnable {

        @Override
        public void run() {
            stopRequestListener.waitingForStop();
            
            lock.lock();
            try {
                stopped = true;
                gotStopRequest.signal();
                
            } finally {
                lock.unlock();
                
            }
        }
        
    }
    
    private class TimeCountingTask implements Runnable {

        @Override
        public void run() {
            int current = seconds.incrementAndGet();
            timeTickingListener.tick(current);
        }
        
    }
    
}
